/******************************************************************
 * @version      : 1.0
 * @Author       : zhl
 * @Date         : 2022-12-06 14:58:35
 * @LastEditors  : zhl
 * @E-Mail       : zhll168@163.com
 * @Description  : 业务描述
 ******************************************************************/

#include "Security-APDU.h"
#include "Server-APDU.h"
#include "Client-APDU.h"
#include "Parse_Packet.h"
#include "common.h"


#define fill_len_field(n) for(int i = n-1; i >= 0; i--){ \
    *((uint8_t *)(apdu+offset)) = num_to_bytes[i];  \
    offset += 1; \
}


Security_APDU Security_APDU::c_security_apdu; 

Security_APDU::Security_APDU(){

}

Security_APDU::~Security_APDU(){

}

Security_APDU *Security_APDU::getInstance(){
    return &c_security_apdu;
}

int Security_APDU::parse_security_response(int64_t dev_id, uint8_t *apdu, int len, std::list<Data> &result){
    if((NULL == apdu) || (1 > len)){
        return -1;
    }
    //
    uint8_t dyn_len = *((uint8_t *)(apdu));
    int  llen = 0;
    if(0x80 == (dyn_len & 0x80)){
        int llen = 0x7F & dyn_len;
        uint8_t v[llen] = {0};
        memcpy(v, apdu+sizeof(uint8_t), llen);
        len = (long long)v;
    } else {
        len = dyn_len;
    }
    return Server_APDU::getInstance().parse_apdu(dev_id, apdu+sizeof(uint8_t)+llen, len, result);
}

int Security_APDU::parse_apdu(int64_t dev_id, uint8_t *apdu, int len, std::list<Data> &result){

    if((NULL == apdu) || (1 > len)){
        return -1;
    }
    uint8_t adu =*((uint8_t*)apdu);
    if(adu == ADU_TYPE_PLAINTEXT){
        return parse_security_response(dev_id, apdu+sizeof(adu), len, result);
    }else if(adu == ADU_TYPE_CIPHERTEXT){
        return -1;
    }else if(adu == ADU_TYPE_ERROR){
        return -1;
    }
    return -1;
}

int Security_APDU::get_request_assemble_verify_unit(int verify_unit, uint8_t *apdu, int len){
    if((NULL == apdu) || (1 > len)){
        return -1;
    }
    switch(verify_unit){
        case 0:

        break;
        case 1:{
            char RN[32] = {0};
            get_rand_str(RN, 16);
            *((uint8_t *)apdu) = 16;
            memcpy(apdu+1, RN, 16);
            return 17;
        }
        break;
        case 2:
        break;
        case 3:
        break;
    }
    return -1;
}

int Security_APDU::get_request_assemble(std::shared_ptr<CConfigManager> cm, TASK_PARAMS *task_params, std::weak_ptr<TERMINAL_DEVICE> dev, uint8_t *apdu, int len){
    if((NULL == task_params) || (task_params->items.empty()) || (dev.expired()) || (NULL == apdu) || (1 > len)){
        return -1;
    }
    int content_max_len = 6000;
    int offset = 0;
    *((uint8_t *)apdu) = SECURITY_REQUEST;
    offset += sizeof(APDU_TAG_TYPE);
    *((uint8_t *)apdu+offset) = dev.lock()->security_data_unit;
    offset += sizeof(uint8_t);

    uint8_t* content = (uint8_t *)malloc(content_max_len);
    int ret = Client_APDU::getInstance()->get_request_assemble(cm, task_params, content, content_max_len);
    if(1 > ret){
        if(NULL != content){
            free(content);
        }
        return -1;
    }
    uint8_t num_to_bytes[4];
    memcpy(num_to_bytes, &ret, sizeof(num_to_bytes));
    if(0x80 > ret){
        *((uint8_t *)(apdu+offset)) = ret;
        offset += 1;
    } else if((0x7F < ret) && (0x100 > ret)){
        *((uint8_t *)(apdu+offset)) = 0x81;
        offset += 1;
        fill_len_field(1);
    } else if((0xFF < ret) && (0x10000 > ret)){
        *((uint8_t *)(apdu+offset)) = 0x82;
        offset += 1;
        fill_len_field(2);
    } else if((0xFFFF < ret) && (0x1000000 > ret)){
        *((uint8_t *)(apdu+offset)) = 0x83;
        offset += 1;
        fill_len_field(3);
    } else if((0xFFFFFF < ret) && (0x10000000 > ret)){
        *((uint8_t *)(apdu+offset)) = 0x84;
        offset += 1;
        fill_len_field(4);
    } 
    memcpy(apdu+offset, content, ret);
    if(NULL != content){
        free(content);
    }
    offset += ret;

    *((uint8_t *)apdu+offset) = dev.lock()->security_verify_unit;
    offset += 1;
    ret = get_request_assemble_verify_unit(dev.lock()->security_verify_unit, apdu+offset, len-offset);
    if(1 > ret){
        return -1;
    }
    
    return (offset+ret);
}